#include "snake.hpp"
#include "../score/score.hpp"

Snake::Snake() {

}

Snake::~Snake() {

}

void Snake::init(Engine *engine) {
	for(std::map<int, Player>::iterator iPlayer = engine->players.begin(); iPlayer != engine->players.end(); iPlayer++) {
		sf::Sprite ball;
		ball.SetImage(engine->getImage("data/snake/ball.png"));
		//ball.SetCenter(sf::Vector2f(16.f, 16.f));
		ball.SetPosition(sf::Vector2f(16.f * sf::Randomizer::Random(1, 48), 16.f * sf::Randomizer::Random(1, 35)));
		ball.SetColor((*iPlayer).second.color);
		for(int i = 0; i < 8; i++) worms[(*iPlayer).first].balls.push_back(ball);

		if(ball.GetPosition().y > 300.f) {
			worms[(*iPlayer).first].direction = 0;
			worms[(*iPlayer).first].balls[0].Move(sf::Vector2f(0.f, -16.f));
		} else {
			worms[(*iPlayer).first].direction = 2;
			worms[(*iPlayer).first].balls[0].Move(sf::Vector2f(0.f, 16.f));
		}


		worms[(*iPlayer).first].alive = true;
	}

	fruit.SetImage(engine->getImage("data/snake/fruit.png"));
	//fruit.SetCenter(sf::Vector2f(16.f, 16.f));
	fruit.SetPosition(sf::Vector2f(16.f * sf::Randomizer::Random(1, 48), 16.f * sf::Randomizer::Random(1, 35)));
	fruitTime = sf::Randomizer::Random(1.f, 5.f);
	fruitSize = sf::Randomizer::Random(2, 8);

	background.SetImage(engine->getImage("data/snake/background.png"));

	gameSpeed = 0.5f;
	quitTime = 5.f;
	quit = false;

	crunch.SetBuffer(engine->getSound("data/snake/crunch.wav"));
	dead.SetBuffer(engine->getSound("data/snake/dead.wav"));

	clock.Reset();
}

void Snake::cleanup() {
	worms.clear();
	scoreStrings.clear();
}

void Snake::pause() {

}

void Snake::resume() {
	clock.Reset();
}

void Snake::processEvent(Engine *engine, PlayerEvent& event) {
	switch(event.type) {
		case PlayerEvent::Quit:
			if(event.state)
				engine->changeState(Score::getSingleton());
			break;
		case PlayerEvent::MoveLeft:
			if(event.state)
				if((worms[event.player].balls[0].GetPosition().x - 16.f != worms[event.player].balls[1].GetPosition().x)
				|| (worms[event.player].balls[0].GetPosition().y != worms[event.player].balls[1].GetPosition().y))
					worms[event.player].direction = 3;
			break;
		case PlayerEvent::MoveRight:
			if(event.state)
				if((worms[event.player].balls[0].GetPosition().x + 16.f != worms[event.player].balls[1].GetPosition().x)
				|| (worms[event.player].balls[0].GetPosition().y != worms[event.player].balls[1].GetPosition().y))
				worms[event.player].direction = 1;
			break;
		case PlayerEvent::MoveUp:
			if(event.state)
				if((worms[event.player].balls[0].GetPosition().x != worms[event.player].balls[1].GetPosition().x)
				|| (worms[event.player].balls[0].GetPosition().y - 16.f != worms[event.player].balls[1].GetPosition().y))
				worms[event.player].direction = 0;
			break;
		case PlayerEvent::MoveDown:
			if(event.state)
				if((worms[event.player].balls[0].GetPosition().x != worms[event.player].balls[1].GetPosition().x)
				|| (worms[event.player].balls[0].GetPosition().y + 16.f != worms[event.player].balls[1].GetPosition().y))
				worms[event.player].direction = 2;
			break;
		default:
			break;
	}
}

void Snake::update(Engine *engine) {
	// computer
	for(std::map<int, Worm>::iterator iWorm = worms.begin(); iWorm != worms.end(); iWorm++) {
		Worm& worm = (*iWorm).second;

		if(!engine->players[(*iWorm).first].computer || !worm.alive) continue;

		if(worm.duration == 0) {
			int direction = 0;

			if((sf::Randomizer::Random(1, 3) < 3) && (fruitTime <= 0.f)) {
				if(sf::Randomizer::Random(1, 2) == 1) {
					if(worm.balls[0].GetPosition().x > fruit.GetPosition().x) {
						direction = 1;
						worm.duration = int(fruit.GetPosition().x - worm.balls[0].GetPosition().x + sf::Randomizer::Random(-2, 2));
					} else if(worm.balls[0].GetPosition().x < fruit.GetPosition().x) {
						direction = 3;
						worm.duration = int(worm.balls[0].GetPosition().x - fruit.GetPosition().x + sf::Randomizer::Random(-2, 2));
					}
				} else {
					if(worm.balls[0].GetPosition().y > fruit.GetPosition().y) {
						direction = 0;
						worm.duration = int(fruit.GetPosition().y - worm.balls[0].GetPosition().y + sf::Randomizer::Random(-2, 2));
					} else if(worm.balls[0].GetPosition().y < fruit.GetPosition().y) {
						direction = 2;
						worm.duration = int(worm.balls[0].GetPosition().y - fruit.GetPosition().y + sf::Randomizer::Random(-2, 2));
					}
				}
			} else {
				worm.duration = sf::Randomizer::Random(1, 20);
				direction = sf::Randomizer::Random(0, 3);
			}

			while(direction % 2 == worm.direction % 2) {
				direction = sf::Randomizer::Random(0, 3);
			}

			worm.direction = direction;
		}

		if((worm.balls[0].GetPosition().x / 16 < 2) && (worm.direction == 3)) {
			worm.direction = sf::Randomizer::Random(0, 1) * 2;
			worm.duration = sf::Randomizer::Random(1, 20);
		} else if((worm.balls[0].GetPosition().x / 16 > 47) && (worm.direction == 1)) {
			worm.direction = sf::Randomizer::Random(0, 1) * 2;
			worm.duration = sf::Randomizer::Random(1, 20);
		} else if((worm.balls[0].GetPosition().y / 16 < 2) && (worm.direction == 0)) {
			worm.direction = sf::Randomizer::Random(0, 1) * 2 + 1;
			worm.duration = sf::Randomizer::Random(1, 20);
		} else if((worm.balls[0].GetPosition().y / 16 > 34) && (worm.direction == 2)) {
			worm.direction = sf::Randomizer::Random(0, 1) * 2 + 1;
			worm.duration = sf::Randomizer::Random(1, 20);
		}
	}

	float elapsedTime = clock.GetElapsedTime();
	clock.Reset();

	lastTime += elapsedTime;

	if(quit) {
		quitTime -= elapsedTime;
		if(quitTime <= 0.f) engine->changeState(Score::getSingleton());
	}

	if(fruitTime > 0.f) fruitTime -= elapsedTime;

	for(std::list<ScoreString>::iterator iScoreString = scoreStrings.begin(); iScoreString != scoreStrings.end(); iScoreString++) {
		(*iScoreString).time -= elapsedTime;
		if((*iScoreString).time <= 0.f) {
			iScoreString = scoreStrings.erase(iScoreString);
			if(iScoreString == scoreStrings.end()) break;
			continue;
		}

		(*iScoreString).string.Move(sf::Vector2f(0.f, -elapsedTime * 32.f));
		(*iScoreString).string.SetColor(sf::Color(255, 255, 255, int(((*iScoreString).time) * 255)));
	}

	if(gameSpeed < 1.f) gameSpeed += elapsedTime / 10.f;
	else gameSpeed = 1.f;

	while(lastTime > 0.1f / gameSpeed) {
		lastTime -= 0.1f / gameSpeed;

		for(std::map<int, Worm>::iterator iWorm = worms.begin(); iWorm != worms.end(); iWorm++) {
			if(!(*iWorm).second.alive) continue;
			if((*iWorm).second.duration > 0) (*iWorm).second.duration--;

			for(std::vector<sf::Sprite>::reverse_iterator iBall = (*iWorm).second.balls.rbegin(); iBall != (*iWorm).second.balls.rend() - 1; iBall++)
				if(((*(iBall)).GetPosition().x != (*(iBall + 1)).GetPosition().x)
				|| ((*(iBall)).GetPosition().y != (*(iBall + 1)).GetPosition().y))
					(*(iBall)).SetPosition((*(iBall + 1)).GetPosition());

			switch((*iWorm).second.direction) {
				case 0:
					(*iWorm).second.balls[0].Move(sf::Vector2f(0.f, -16.f));
					break;
				case 1:
					(*iWorm).second.balls[0].Move(sf::Vector2f(16.f, 0.f));
					break;
				case 2:
					(*iWorm).second.balls[0].Move(sf::Vector2f(0.f, 16.f));
					break;
				case 3:
					(*iWorm).second.balls[0].Move(sf::Vector2f(-16.f, 0.f));
					break;
			}

		}
	}

	int alives = 0;
	for(std::map<int, Worm>::iterator iWorm = worms.begin(); iWorm != worms.end(); iWorm++) {
		if(!(*iWorm).second.alive) continue;

		if(((*iWorm).second.balls[0].GetPosition().x / 16 < 1) || ((*iWorm).second.balls[0].GetPosition().x / 16 > 48)
		|| ((*iWorm).second.balls[0].GetPosition().y / 16 < 1) || ((*iWorm).second.balls[0].GetPosition().y / 16 > 35)) {
			dead.Play();
			addScore(engine, (*iWorm).first, -5);
			(*iWorm).second.alive = false;
			continue;
		}

		for(std::map<int, Worm>::iterator jWorm = worms.begin(); jWorm != worms.end(); jWorm++) {
			if(!(*jWorm).second.alive) continue;

			for(std::vector<sf::Sprite>::iterator iBall = (*jWorm).second.balls.begin(); iBall != (*jWorm).second.balls.end(); iBall++) {
				if((iWorm == jWorm) && (iBall == (*jWorm).second.balls.begin())) continue;

				if((int((*iWorm).second.balls[0].GetPosition().x) == int((*iBall).GetPosition().x))
				&& (int((*iWorm).second.balls[0].GetPosition().y) == int((*iBall).GetPosition().y))) {
					dead.Play();
					(*iWorm).second.alive = false;
					if(iWorm == jWorm) addScore(engine, (*iWorm).first, -5);
					else if(iBall == (*jWorm).second.balls.begin()) (*jWorm).second.alive = false;
					else addScore(engine, (*jWorm).first, 10);

					break;
				}
			}
		}

		if(fruitTime < 0.f)
			if((int((*iWorm).second.balls[0].GetPosition().x) == int(fruit.GetPosition().x))
			&& (int((*iWorm).second.balls[0].GetPosition().y) == int(fruit.GetPosition().y))) {
				crunch.Play();
				sf::Sprite ball;
				ball.SetImage(engine->getImage("data/snake/ball.png"));
				ball.SetPosition((*iWorm).second.balls.back().GetPosition());
				ball.SetColor(engine->players[(*iWorm).first].color);
				for(int i = 0; i < fruitSize; i++) (*iWorm).second.balls.push_back(ball);

				addScore(engine, (*iWorm).first, fruitSize);

				fruit.SetPosition(sf::Vector2f(16.f * sf::Randomizer::Random(1, 48), 16.f * sf::Randomizer::Random(1, 35)));
				fruitTime = sf::Randomizer::Random(1.f, 5.f);
				fruitSize = sf::Randomizer::Random(2, 8);
			}

		if((*iWorm).second.alive) alives++;
	}

	if((!quit) && (alives <= 1)) quit = true;
}

void Snake::draw(Engine *engine) {
	engine->window.Draw(background);

	if(fruitTime < 0.f) engine->window.Draw(fruit);

	for(std::map<int, Worm>::iterator iWorm = worms.begin(); iWorm != worms.end(); iWorm++) {
		if(!(*iWorm).second.alive) continue;
		for(std::vector<sf::Sprite>::iterator iBall = (*iWorm).second.balls.begin(); iBall != (*iWorm).second.balls.end(); iBall++)
			engine->window.Draw((*iBall));
	}

	for(std::list<ScoreString>::iterator iScoreString = scoreStrings.begin(); iScoreString != scoreStrings.end(); iScoreString++)
		engine->window.Draw((*iScoreString).string);
}

void Snake::addScore(Engine *engine, int player, int score) {
	engine->players[player].score += score;
	if(engine->players[player].score < 0) engine->players[player].score = 0;

	sf::String string;
	string.SetText(val2str(score));
	if(score > 0) string.SetText("+" + string.GetText());
	string.SetFont(engine->getFont("data/fonts/BELIEVER.TTF"));
	string.SetSize(24.f);
	string.SetColor(sf::Color(255, 255, 255, 255));
	string.SetPosition(worms[player].balls[0].GetPosition());

	if(string.GetPosition().x + string.GetRect().GetWidth() > 800.f) string.Move(sf::Vector2f(800.f - (string.GetPosition().x + string.GetRect().GetWidth()), 0.f));
	if(string.GetPosition().y + string.GetRect().GetHeight() > 600.f) string.Move(sf::Vector2f(0.f, 600.f - (string.GetPosition().y + string.GetRect().GetHeight())));

	scoreStrings.push_back(ScoreString(string));
}
